package com.cqc.jdk8.Jmath;

import org.apache.commons.lang3.StringUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.NumberFormat;

/**
 * 金额处理帮助类
 */
public class BigDecimalUtil {

    /**
     * 方法说明：金额扩大100倍，一般用来插入数据库前格式化字符串，配合方法 divide100使用
     * 备注：
     * 1 amount为空，抛出异常
     * 2 扩大100倍后小数位直接舍掉
     *
     * @param amount 要格式化的金额字符串
     * @return 扩大100倍后的金额格式
     */
    public static String multiply100(String amount) {
        if (StringUtils.isBlank(amount)) {
            throw new IllegalArgumentException("Args can not be null");
        }
        //小数位后舍掉
        return new BigDecimal(amount).multiply(new BigDecimal("100")).setScale(0, BigDecimal.ROUND_DOWN).toString();
    }

    /**
     * 方法说明： 金额缩写100倍
     * 备注：amount为空，抛出异常
     *
     * @param amount 要格式化的金额字符串
     * @return 缩小100倍后的金额格式
     */
    public static String divide100(String amount) {
        if (StringUtils.isBlank(amount)) {
            throw new IllegalArgumentException("Args can not be null");
        }
        return new BigDecimal(amount).divide(new BigDecimal("100")).toString();
    }


    /**
     * 方法说明： 得到财务常用金额格式
     * 备注：
     * 1 amount为空，抛出异常
     * 2 每3位使用都好隔开
     * 3 小数点后固定长度2位，不足补零，超过的直接舍掉
     *
     * @param amount 要格式化的金额字符串
     * @return 财务常用金额格式
     */
    public static String formatFinanceAmount(String amount) {
        if (StringUtils.isBlank(amount)) {
            throw new IllegalArgumentException("Args can not be null");
        }
        NumberFormat fmt = new DecimalFormat("###,##0.00");
        fmt.setRoundingMode(RoundingMode.DOWN);
        return fmt.format(new BigDecimal(amount));
    }

    /**
     * 方法说明： 得到金额中文大写
     * 备注：
     * 1 amount为空，返回空串
     * 2 只接收整数或者零作为参数，否则抛出异常
     * 3 最小单位是分，分单位后的厘直接舍去
     * 4 最多支持小数点前16位
     *
     * @param amount 要格式化的金额字符串
     * @return 金额中文大写字符串
     */
    public static String formatCHSAmount(String amount) {
        if (StringUtils.isBlank(amount)) {
            return "";
        }
        return BigDecimalUtil.cleanZero(BigDecimalUtil.splitNum(BigDecimalUtil.roundString(BigDecimalUtil.getNum(amount.trim()))));
    }

    /**
     * 从命令行接收一个数，在其中调用 checkNum() 方法对其进行 验证，并返回相应的值
     *
     * @return 如果输入合法，返回输入的这个数
     */
    private static String getNum(String s) {
        //判断用户输入是否合法
        //若合法，返回这个值；若非法，返回“0”
        if (checkNum(s)) {
            return s;
        } else {
            throw new IllegalArgumentException("金额必须是零或者整数");
        }
    }

    /**
     * 判断用户输入的数据是否合法，用户只能输入大于0的数字，不能输入其它字符
     *
     * @return 数据合法，返回true
     */
    private static boolean checkNum(String s) {
        //如果用户输入的数里有非数字字符，则视为非法数据，返回false
        try {
            double d = Double.valueOf(s);
            //如果这个数小于零，则视为非法数据，返回false
            if (d < 0) {
                return false;
            } else {
                return true;
            }
        } catch (NumberFormatException e) {
            return false;
        }
    }

    /**
     * 对传入的数进行四舍五入操作(保留2位小数)
     *
     * @param s 参数
     * @return 新值
     */
    private static String roundString(String s) {
        //如果传入的是空字符串，则返回空串
        if ("".equals(s)) {
            return s;
        }
        s = new BigDecimal(s).setScale(2, BigDecimal.ROUND_DOWN).toString();
        //以小数点位届分割这个字符串
        int index = s.indexOf(".");
        //这个数的整数部分
        String intOnly = s.substring(0, index);
        //规定数字的最大长度只能到 万亿单位，否则抛出异常
        if (intOnly.length() > 16) {
            throw new IllegalArgumentException("金额过大,整数部分最多16位。");
        }
        //这个数的小数部分
        String smallOnly = s.substring(index + 1);
        //如果小数部分大于2位，则只要前2位
        if (smallOnly.length() > 2) {
            String roundSmall = smallOnly.substring(0, 2);
            //把整数部分和新截取的小数部分重新拼凑这个字符串
            s = intOnly + "." + roundSmall;
        }
        return s;
    }

    /**
     * 把用户输入的数以小数点为届分割开来，并调用 numFormat()方法 进行相应的中文金额转换，
     * 注： 传入的这个数应该是经过 roundString()方法进行了四舍五入操作的
     *
     * @param s 参数
     * @return 转换好的在的中文金额大写形式的字符串
     */
    private static String splitNum(String s) {
        //如果传入的是空字符串，则返回空串
        if ("".equals(s)) {
            return s;
        }
        //以小数点位届分割这个字符串
        int index = s.indexOf(".");
        //这个数的整数部分
        String intOnly = s.substring(0, index);
        String part1 = numFormat(1, intOnly);
        //这个数的小数部分
        String smallOnly = s.substring(index + 1);
        String part2 = numFormat(2, smallOnly);
        return part1 + part2;
    }

    /**
     * 转换成中文金额大写形式
     *
     * @param flag 标志位 1=整数部分，2=小数部分
     * @param s    参数
     * @return 转换好的带单位的中文金额大写形式
     */
    private static String numFormat(int flag, String s) {
        int sLength = s.length();
        //货币大写形式
        String[] bigLetter = {"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"};
        //货币单位
        String[] unit = {"元", "拾", "佰", "仟", "万",
                //拾万位 到 仟万位
                "拾", "佰", "仟",
                //亿位 到 万亿位 ?
                "亿", "拾", "佰", "仟", "万", "拾", "佰", "仟", "万", "拾", "佰", "仟"};
        String[] small = {"分", "角"};
        //用来存放转换后的新字符串
        String newStr = "";
        //逐位替换位中文大写形式
        for (int i = 0; i < sLength; i++) {
            if (flag == 1) {
                //转换整数部分为中文大写形式(带单位)
                newStr = newStr + bigLetter[s.charAt(i) - 48] + unit[sLength - i - 1];
            } else if (flag == 2) {
                //转换小数部分为中文大写形式(带单位)
                newStr = newStr + bigLetter[s.charAt(i) - 48] + small[sLength - i - 1];
            }
        }
        return newStr;
    }

    /**
     * 把已经转换好的中文金额大写形式 加以改进，清零里面多余的零，
     * 把这个字符串变的更加可观
     * 注： 参数必须是经过splitNum()方法处理的，这个字符串应该是中文金额大写形式的。
     *
     * @param s 入参
     */
    private static String cleanZero(String s) {
        //如果传入的是空字符串，则返回空串
        if ("".equals(s)) {
            return s;
        }
        //如果用户输入了多个0，去掉前面的零，使其看上去符合习惯
        while (s.charAt(0) == '零') {
            //去掉  字符串前面的 “零” 和它对应的单位
            s = s.substring(2);
            //如果用户当初输入的时候只输入了0，则只返回一个 “零”
            if (s.length() == 0) {
                return "零元整";
            }
        }
        //字符串中存在多个‘零’在一起的时候只读出一个‘零’，并省略多余的单位
        String regex1[] = {"零仟", "零佰", "零拾"};
        String regex2[] = {"零亿", "零万", "零元"};
        String regex3[] = {"亿", "万", "元"};
        String regex4[] = {"零角", "零分"};
        //第一轮转换把 "零仟", "零佰", "零拾"  等替换成一个零
        int regex1Length = regex1.length;
        for (int i = 0; i < regex1Length; i++) {
            s = s.replaceAll(regex1[i], "零");
        }
        //第二轮转换考虑 "零亿", "零万", "零元" 等情况
        //"亿", "万", "元" 有些情况需要保留
        int regex2Length = regex2.length;
        for (int i = 0; i < regex2Length; i++) {
            //当第一轮转换后可能有多个零叠加在一起，需要变成一个零
            s = s.replaceAll("零零零", "零");
            s = s.replaceAll("零零", "零");
            s = s.replaceAll(regex2[i], regex3[i]);
        }
        //第三轮转换 去掉 "零角", "零分"
        int regex4Length = regex4.length;
        for (int i = 0; i < regex4Length; i++) {
            s = s.replaceAll(regex4[i], "");
        }
        //当万到亿之间全是零的时候，忽略“亿万”单位，只保留一个“亿”
        s = s.replaceAll("亿万", "亿");
        //如果没有角和分，那么加上整
        if (!s.contains("角") && !s.contains("分")) {
            s = s + "整";
        }
        return s;
    }
}
